#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include <curl/curl.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <libayatana-appindicator/app-indicator.h>
#include <libgen.h>
#include <limits.h>
#include <locale.h>
#include <mpv/client.h>
#include <openssl/evp.h>
#include <openssl/md5.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#define CACHE_DIR ".cache/peruere"
#define CACHE_INDEX "index"

typedef struct {
  char file_path[1024];
  char url[1024];
  char geometry[32];
  bool keep_cache;
} Config;

typedef struct {
  char *history[30];
  int idx;
  int count;
} InputHistory;

typedef struct {
  GtkWidget *widget;
  gboolean sensitive;
} SensitiveData;

typedef struct {
  GtkToggleButton *button;
  gboolean active;
} ToggleData;

// 全局变量
static Config cfg;
static GtkWidget *main_window = NULL, *help_window = NULL, *log_window = NULL;
static GtkWidget *entry_file = NULL, *entry_url = NULL, *entry_geo = NULL;
static GtkWidget *btn_browse = NULL, *btn_play = NULL, *btn_stop = NULL;
static GtkWidget *btn_clean = NULL, *btn_help = NULL, *btn_log = NULL;
static GtkWidget *chk_keep_cache = NULL, *lbl_error = NULL, *chk_mpv_log = NULL;
static GtkWidget *progress_bar = NULL, *lbl_progress = NULL;
static InputHistory hist_file, hist_url, hist_geo;
static AppIndicator *tray_indicator = NULL;
static GtkTextBuffer *log_buffer = NULL;

static mpv_handle *mpv_inst = NULL;
static pthread_t mpv_event_thread;
static pthread_t download_thread;
static volatile int quit_flag = 0;
static volatile int is_playing = 0;
static volatile int is_downloading = 0;

static CURL *curl_handle = NULL;
static curl_off_t download_total = 0;
static curl_off_t download_current = 0;
static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
static char temp_path[PATH_MAX] = {0};
static char cache_path[PATH_MAX] = {0};
static int temp_fd = -1;

static Display *x11_display = NULL;
static Window x11_window = None;

static const char *help_text =
    "Usage:\n"
    "  - Select local file or enter URL (choose one)\n"
    "  - Window geometry: widthxheight+X+Y (default 1920x1080+0+0)\n"
    "  - Keep Cache: Only available for URL (reuse cache for same URL)\n"
    "  - Show MPV Debug Log: Enable/disable detailed MPV logs\n"
    "  - Clean cache: Delete all cached files\n"
    "  - Press ESC to close help\n"
    "  - Ctrl+C/V/X/A/Z: Copy/Paste/Cut/SelectAll/Undo";

// 提前定义关键函数（解决循环依赖）
static void set_download_progress(int progress);
static int mpv_play_video(const char *play_path, int width, int height, int x,
                          int y);
static void on_stop_clicked(GtkWidget *w, gpointer d);
static void update_input_states(void);
static void on_mpv_log_toggled(GtkWidget *widget,
                               gpointer data); // 新增：MPV日志开关回调

// 输入历史相关函数
static void input_history_init(InputHistory *hist) {
  if (!hist)
    return;
  memset(hist->history, 0, sizeof(hist->history));
  hist->idx = 0;
  hist->count = 0;
}

static void input_history_save(InputHistory *hist, const char *text) {
  if (!hist || !text)
    return;

  if (hist->count >= 30) {
    if (hist->history[0])
      free(hist->history[0]);
    for (int i = 0; i < 29; i++)
      hist->history[i] = hist->history[i + 1];
    hist->count--;
    hist->idx--;
  }

  char truncated[1024];
  strncpy(truncated, text, 1023);
  truncated[1023] = '\0';
  hist->history[hist->count] = strdup(truncated);
  hist->count++;
  hist->idx = hist->count;
}

static void input_history_undo(InputHistory *hist, GtkWidget *entry) {
  if (!hist || !entry || !GTK_IS_ENTRY(entry))
    return;
  if (hist->idx <= 0 || hist->count == 0)
    return;

  hist->idx--;
  if (hist->history[hist->idx])
    gtk_entry_set_text(GTK_ENTRY(entry), hist->history[hist->idx]);
}

// 错误处理函数
static gboolean error_idle_func(gpointer data) {
  const char *msg = (const char *)data;
  gtk_label_set_text(GTK_LABEL(lbl_error), msg ? msg : "");
  gtk_widget_show(lbl_error);
  static guint error_timeout = 0;
  if (error_timeout)
    g_source_remove(error_timeout);
  error_timeout =
      g_timeout_add_seconds(3, (GSourceFunc)gtk_widget_hide, lbl_error);
  g_free((gpointer)msg);
  return FALSE;
}

static gboolean set_sensitive_idle(gpointer data) {
  SensitiveData *sd = (SensitiveData *)data;
  gtk_widget_set_sensitive(sd->widget, sd->sensitive);
  g_free(sd); // 释放内存
  return FALSE;
}

static void set_error(const char *msg) {
  if (!lbl_error || !GTK_IS_LABEL(lbl_error))
    return;
  g_idle_add(error_idle_func, g_strdup(msg));
}

// 进度条处理函数
static gboolean progress_idle_func(gpointer data) {
  int progress = GPOINTER_TO_INT(data);
  if (progress < 0) {
    gtk_widget_hide(progress_bar);
    gtk_widget_hide(lbl_progress);
    return FALSE;
  }

  gtk_widget_show(progress_bar);
  gtk_widget_show(lbl_progress);

  if (progress == 101) {
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 1.0);
    gtk_label_set_text(GTK_LABEL(lbl_progress), "Download completed!");
    static guint complete_timeout = 0;
    if (complete_timeout)
      g_source_remove(complete_timeout);
    complete_timeout = g_timeout_add_seconds(
        3, (GSourceFunc)set_download_progress, GINT_TO_POINTER(-1));
  } else {
    gdouble frac = CLAMP((gdouble)progress / 100.0, 0.0, 1.0);
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), frac);
    char buf[32];
    snprintf(buf, sizeof(buf) - 1, "Download: %d%%", progress);
    gtk_label_set_text(GTK_LABEL(lbl_progress), buf);
  }
  return FALSE;
}

static void set_download_progress(int progress) {
  if (!progress_bar || !lbl_progress)
    return;
  g_idle_add(progress_idle_func, GINT_TO_POINTER(progress));
}

// 日志处理函数
static gboolean log_idle_func(gpointer data) {
  char *log_msg = (char *)data;
  GtkTextIter iter;
  gtk_text_buffer_get_end_iter(log_buffer, &iter);
  gtk_text_buffer_insert(log_buffer, &iter, log_msg, -1);
  g_free(log_msg);
  return FALSE;
}

static gboolean set_toggle_active_idle(gpointer data) {
  ToggleData *td = (ToggleData *)data;
  gtk_toggle_button_set_active(td->button, td->active);
  g_free(td); // 释放内存
  return FALSE;
}

static void log_print(const char *level, const char *msg) {
  if (!msg)
    return;
  pthread_mutex_lock(&log_mutex);
  char timestamp[32];
  time_t now = time(NULL);
  struct tm *tm = localtime(&now);
  strftime(timestamp, sizeof(timestamp), "[%Y-%m-%d %H:%M:%S]", tm);
  char log_msg[1024];
  snprintf(log_msg, sizeof(log_msg) - 1, "%s [%s] %s\n", timestamp, level, msg);
  log_msg[sizeof(log_msg) - 1] = '\0';
  fprintf(stderr, "%s", log_msg);
  if (log_buffer)
    g_idle_add(log_idle_func, g_strdup(log_msg));
  pthread_mutex_unlock(&log_mutex);
}

// 输入框事件处理
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event,
                             gpointer data) {
  if (!widget || !GTK_IS_ENTRY(widget) || !gtk_widget_has_focus(widget))
    return FALSE;
  if (!(event->state & GDK_CONTROL_MASK))
    return FALSE;

  InputHistory *hist = (InputHistory *)data;
  switch (event->keyval) {
  case GDK_KEY_c:
    gtk_editable_copy_clipboard(GTK_EDITABLE(widget));
    return TRUE;
  case GDK_KEY_v:
    gtk_editable_paste_clipboard(GTK_EDITABLE(widget));
    if (hist)
      input_history_save(hist, gtk_entry_get_text(GTK_ENTRY(widget)));
    return TRUE;
  case GDK_KEY_x:
    gtk_editable_cut_clipboard(GTK_EDITABLE(widget));
    if (hist)
      input_history_save(hist, gtk_entry_get_text(GTK_ENTRY(widget)));
    return TRUE;
  case GDK_KEY_a:
    gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
    return TRUE;
  case GDK_KEY_z:
    if (hist)
      input_history_undo(hist, widget);
    return TRUE;
  default:
    return FALSE;
  }
}

// 输入状态更新（新增功能：输入冲突处理）
static void update_input_states(void) {
  const char *file_text = gtk_entry_get_text(GTK_ENTRY(entry_file));
  const char *url_text = gtk_entry_get_text(GTK_ENTRY(entry_url));
  gboolean file_has_text = strlen(file_text) > 0;
  gboolean url_has_text = strlen(url_text) > 0;

  // 输入冲突处理：一个有内容则禁用另一个（通过包装函数传递参数）
  SensitiveData *sd1 = g_new(SensitiveData, 1);
  sd1->widget = entry_url;
  sd1->sensitive = !file_has_text;
  g_idle_add(set_sensitive_idle, sd1);

  SensitiveData *sd2 = g_new(SensitiveData, 1);
  sd2->widget = btn_browse;
  sd2->sensitive = !url_has_text;
  g_idle_add(set_sensitive_idle, sd2);

  SensitiveData *sd3 = g_new(SensitiveData, 1);
  sd3->widget = entry_file;
  sd3->sensitive = !url_has_text;
  g_idle_add(set_sensitive_idle, sd3);

  // Keep Cache仅对URL启用
  SensitiveData *sd4 = g_new(SensitiveData, 1);
  sd4->widget = chk_keep_cache;
  sd4->sensitive = url_has_text;
  g_idle_add(set_sensitive_idle, sd4);

  // 选本地文件时强制取消Keep Cache勾选
  if (file_has_text) {
    ToggleData *td = g_new(ToggleData, 1);
    td->button = GTK_TOGGLE_BUTTON(chk_keep_cache);
    td->active = FALSE;
    g_idle_add(set_toggle_active_idle, td);
  }

  // 播放按钮仅在有输入时启用
  SensitiveData *sd5 = g_new(SensitiveData, 1);
  sd5->widget = btn_play;
  sd5->sensitive = file_has_text || url_has_text;
  g_idle_add(set_sensitive_idle, sd5);
}

static void on_entry_changed(GtkWidget *widget, gpointer data) {
  if (!widget || !GTK_IS_ENTRY(widget) || !data)
    return;
  InputHistory *hist = (InputHistory *)data;
  const char *text = gtk_entry_get_text(GTK_ENTRY(widget));

  // 保存历史并更新输入状态
  if (hist->count == 0 || strcmp(text, hist->history[hist->count - 1]) != 0) {
    input_history_save(hist, text);
  }
  update_input_states();
}

// 几何参数解析
static int parse_geometry(const char *geom, int *width, int *height, int *x,
                          int *y) {
  if (sscanf(geom, "%dx%d+%d+%d", width, height, x, y) != 4) {
    set_error("Geometry format invalid (use: widthxheight+X+Y)");
    return -1;
  }
  if (*width <= 0 || *height <= 0) {
    set_error("Width/height must be positive");
    return -1;
  }
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "Window config: w=%d h=%d x=%d y=%d",
           *width, *height, *x, *y);
  log_print("INFO", log_msg);
  return 0;
}

// URL转MD5
static void url_to_md5(const char *url, char *md5_str) {
  EVP_MD_CTX *ctx = EVP_MD_CTX_new();
  if (!ctx) {
    memset(md5_str, 0, 33);
    set_error("Failed to create MD5 context");
    return;
  }

  if (EVP_DigestInit_ex(ctx, EVP_md5(), NULL) != 1) {
    EVP_MD_CTX_free(ctx);
    memset(md5_str, 0, 33);
    set_error("Failed to init MD5");
    return;
  }

  if (EVP_DigestUpdate(ctx, url, strlen(url)) != 1) {
    EVP_MD_CTX_free(ctx);
    memset(md5_str, 0, 33);
    set_error("Failed to update MD5");
    return;
  }

  unsigned char md5_hash[EVP_MAX_MD_SIZE];
  unsigned int md5_len;
  if (EVP_DigestFinal_ex(ctx, md5_hash, &md5_len) != 1 ||
      md5_len != MD5_DIGEST_LENGTH) {
    EVP_MD_CTX_free(ctx);
    memset(md5_str, 0, 33);
    set_error("Failed to compute MD5");
    return;
  }
  EVP_MD_CTX_free(ctx);

  for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
    sprintf(md5_str + 2 * i, "%02x", md5_hash[i]);
  }
  md5_str[32] = '\0';
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "URL=%s MD5=%s", url, md5_str);
  log_print("INFO", log_msg);
}

// 缓存目录初始化
static int init_cache_dir() {
  char *home = getenv("HOME");
  if (!home) {
    set_error("Failed to get HOME directory");
    return -1;
  }

  char full_cache_dir[PATH_MAX];
  snprintf(full_cache_dir, PATH_MAX, "%s/%s", home, CACHE_DIR);
  log_print("INFO", full_cache_dir);

  char *dir = strdup(full_cache_dir);
  char *p = dir;
  while (*p) {
    if (*p == '/') {
      *p = '\0';
      if (strlen(dir) > 0 && mkdir(dir, 0755) != 0 && errno != EEXIST) {
        char err_msg[256];
        snprintf(err_msg, sizeof(err_msg), "Failed to create dir: %s", dir);
        set_error(err_msg);
        free(dir);
        return -1;
      }
      *p = '/';
    }
    p++;
  }

  if (mkdir(full_cache_dir, 0755) != 0 && errno != EEXIST) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Failed to create cache dir: %s",
             full_cache_dir);
    set_error(err_msg);
    free(dir);
    return -1;
  }
  free(dir);
  return 0;
}

// 目录删除
static int remove_dir(const char *path) {
  DIR *dir = opendir(path);
  if (!dir) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Failed to open dir: %s", path);
    set_error(err_msg);
    return -1;
  }

  struct dirent *entry;
  while ((entry = readdir(dir)) != NULL) {
    if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
      continue;

    char full_path[PATH_MAX];
    snprintf(full_path, PATH_MAX, "%s/%s", path, entry->d_name);

    struct stat st;
    if (stat(full_path, &st) != 0) {
      char err_msg[256];
      snprintf(err_msg, sizeof(err_msg), "Failed to stat file: %s", full_path);
      set_error(err_msg);
      continue;
    }

    if (S_ISDIR(st.st_mode)) {
      if (remove_dir(full_path) != 0) {
        closedir(dir);
        return -1;
      }
    } else {
      if (unlink(full_path) != 0) {
        char err_msg[256];
        snprintf(err_msg, sizeof(err_msg), "Failed to delete file: %s",
                 full_path);
        set_error(err_msg);
      } else {
        log_print("INFO", full_path);
      }
    }
  }

  closedir(dir);

  if (rmdir(path) != 0) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Failed to delete dir: %s", path);
    set_error(err_msg);
    return -1;
  }
  log_print("INFO", path);
  return 0;
}

// 清理缓存
static void clean_all_cache() {
  char *home = getenv("HOME");
  if (!home) {
    set_error("Failed to get HOME directory");
    return;
  }

  char full_cache_dir[PATH_MAX];
  snprintf(full_cache_dir, PATH_MAX, "%s/%s", home, CACHE_DIR);
  log_print("INFO", full_cache_dir);

  struct stat st;
  if (stat(full_cache_dir, &st) != 0) {
    if (errno == ENOENT)
      set_error("Cache dir not exists");
    else {
      char err_msg[256];
      snprintf(err_msg, sizeof(err_msg), "Failed to access cache dir: %s",
               full_cache_dir);
      set_error(err_msg);
    }
    return;
  }

  if (!S_ISDIR(st.st_mode)) {
    set_error("Cache path is not a directory");
    return;
  }

  if (remove_dir(full_cache_dir) == 0)
    set_error("Cache cleaned successfully");
  else
    set_error("Failed to clean cache");
}

// 查找缓存的URL
static int find_cached_url(const char *url, char *cache_path) {
  char *home = getenv("HOME");
  if (!home) {
    set_error("Failed to get HOME directory");
    return -1;
  }

  char index_path[PATH_MAX];
  snprintf(index_path, PATH_MAX, "%s/%s/%s", home, CACHE_DIR, CACHE_INDEX);
  log_print("INFO", index_path);

  FILE *index = fopen(index_path, "r");
  if (!index) {
    log_print("INFO", "Cache index not found");
    return -1;
  }

  char line[1024];
  while (fgets(line, sizeof(line), index)) {
    char stored_url[1024], stored_md5[33];
    if (sscanf(line, "%32s %s", stored_md5, stored_url) == 2 &&
        strcmp(stored_url, url) == 0) {
      snprintf(cache_path, PATH_MAX, "%s/%s/%s.mp4", home, CACHE_DIR,
               stored_md5);
      fclose(index);
      log_print("INFO", cache_path);
      return 0;
    }
  }
  fclose(index);
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "No cache found for URL: %s", url);
  log_print("INFO", log_msg);
  return -1;
}

// 添加到缓存索引
static int add_to_cache_index(const char *url, const char *md5) {
  if (init_cache_dir() != 0)
    return -1;

  char *home = getenv("HOME");
  char index_path[PATH_MAX];
  snprintf(index_path, PATH_MAX, "%s/%s/%s", home, CACHE_DIR, CACHE_INDEX);

  char existing_path[PATH_MAX];
  if (find_cached_url(url, existing_path) == 0)
    return 0;

  FILE *index = fopen(index_path, "a");
  if (!index) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Failed to open cache index");
    set_error(err_msg);
    return -1;
  }
  fprintf(index, "%s %s\n", md5, url);
  fclose(index);
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "Added to cache index: %s -> %s", md5,
           url);
  log_print("INFO", log_msg);
  return 0;
}

// curl下载回调
static size_t peruere_write_callback(void *ptr, size_t size, size_t nmemb,
                                     void *stream) {
  if (quit_flag || !is_downloading)
    return 0;
  int fd = *(int *)stream;
  size_t written = write(fd, ptr, size * nmemb);
  if (written != size * nmemb)
    set_error("Failed to write downloaded data");
  return written;
}

static int peruere_xferinfo_callback(void *clientp, curl_off_t dltotal,
                                     curl_off_t dlnow, curl_off_t ultotal,
                                     curl_off_t ulnow) {
  (void)clientp;
  (void)ultotal;
  (void)ulnow;

  if (quit_flag || !is_downloading)
    return 1;

  pthread_mutex_lock(&progress_mutex);
  download_total = dltotal;
  download_current = dlnow;
  pthread_mutex_unlock(&progress_mutex);

  if (dltotal > 0)
    set_download_progress((int)((double)dlnow / dltotal * 100));
  else
    set_download_progress(-2);
  return 0;
}

static char* trim_string(const char* str) {
    if (!str) return NULL;
    // 使用GLib的g_strstrip函数（自动去除首尾空白字符）
    return g_strstrip(g_strdup(str));
}

// 下载线程函数
static int download_url_thread(const char *url, char **play_path) {
  if (find_cached_url(url, cache_path) == 0) {
    struct stat st;
    if (stat(cache_path, &st) == 0 && st.st_size > 0) {
      *play_path = cache_path;
      set_error("Using cached file");
      return 0;
    } else {
      unlink(cache_path);
      set_error("Cached file invalid, re-downloading");
    }
  }

  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "Start downloading URL: %s", url);
  log_print("INFO", log_msg);
  curl_handle = curl_easy_init();
  if (!curl_handle) {
    set_error("Failed to init curl");
    return -1;
  }

  int fd = -1;
  char *save_path = NULL;
  char md5_str[33] = {0};
  char temp_template[PATH_MAX];

  if (cfg.keep_cache) {
    url_to_md5(url, md5_str);
    if (init_cache_dir() != 0) {
      curl_easy_cleanup(curl_handle);
      return -1;
    }
    char *home = getenv("HOME");
    snprintf(cache_path, PATH_MAX, "%s/%s/%s.mp4", home, CACHE_DIR, md5_str);
    fd = open(cache_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    save_path = cache_path;
    snprintf(log_msg, sizeof(log_msg), "Cache mode: save to %s", save_path);
    log_print("INFO", log_msg);
  } else {
    snprintf(temp_template, PATH_MAX, "/tmp/peruere-XXXXXX");
    fd = mkstemp(temp_template);
    if (fd < 0) {
      set_error("Failed to create temp file");
      curl_easy_cleanup(curl_handle);
      return -1;
    }
    snprintf(temp_path, PATH_MAX, "%s.mp4", temp_template);
    if (rename(temp_template, temp_path) != 0) {
      strncpy(temp_path, temp_template, PATH_MAX - 1);
      log_print("WARNING", "Temp file rename failed, use original name");
    }
    save_path = temp_path;
    snprintf(log_msg, sizeof(log_msg), "Temp mode: save to %s", save_path);
    log_print("INFO", log_msg);
  }

  if (fd < 0) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Failed to create file: %s", save_path);
    set_error(err_msg);
    curl_easy_cleanup(curl_handle);
    return -1;
  }

  curl_easy_setopt(curl_handle, CURLOPT_URL, url);
  curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, peruere_write_callback);
  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &fd);
  curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
  curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION,
                   peruere_xferinfo_callback);
  curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, NULL);
  curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 10L);
  curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 300L);
  curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_LIMIT, 1024);
  curl_easy_setopt(curl_handle, CURLOPT_LOW_SPEED_TIME, 10);

  CURLcode res = curl_easy_perform(curl_handle);
  close(fd);
  curl_easy_cleanup(curl_handle);
  curl_handle = NULL;

  pthread_mutex_lock(&state_mutex);
  is_downloading = 0;
  // 下载完成后启用播放按钮（GTK主线程）
  SensitiveData *sd = g_new(SensitiveData, 1);
  sd->widget = btn_play;
  sd->sensitive = TRUE;
  g_idle_add(set_sensitive_idle, sd);
  pthread_mutex_unlock(&state_mutex);

  if (quit_flag) {
    set_error("Download interrupted");
    if (!cfg.keep_cache) {
      unlink(save_path);
      snprintf(log_msg, sizeof(log_msg), "Deleted temp file: %s", save_path);
      log_print("INFO", log_msg);
    }
    return -1;
  }

  if (res != CURLE_OK) {
    char err_msg[256];
    snprintf(err_msg, sizeof(err_msg), "Download failed: %s",
             curl_easy_strerror(res));
    set_error(err_msg);
    unlink(save_path);
    snprintf(log_msg, sizeof(log_msg), "Deleted incomplete file: %s",
             save_path);
    log_print("INFO", log_msg);
    return -1;
  }

  if (cfg.keep_cache && strlen(md5_str) > 0)
    add_to_cache_index(url, md5_str);

  snprintf(log_msg, sizeof(log_msg), "Download completed: %s Size: %.2f MB",
           save_path, (double)download_current / 1024 / 1024);
  log_print("INFO", log_msg);
  set_download_progress(101);
  *play_path = save_path;
  return 0;
}

// 创建X11窗口
static Window create_x11_window(int width, int height, int x, int y) {
  x11_display = XOpenDisplay(NULL);
  if (!x11_display) {
    set_error("Failed to open X11 display");
    return None;
  }
  log_print("INFO", DisplayString(x11_display));

  Window root = DefaultRootWindow(x11_display);
  XSetWindowAttributes attrs = {0};
  attrs.background_pixmap = ParentRelative;
  attrs.backing_store = Always;
  attrs.override_redirect = True;

  Window win = XCreateWindow(
      x11_display, root, x, y, width, height, 0,
      DefaultDepth(x11_display, DefaultScreen(x11_display)), InputOutput,
      DefaultVisual(x11_display, DefaultScreen(x11_display)),
      CWOverrideRedirect | CWBackingStore, &attrs);
  if (win == None) {
    set_error("Failed to create X11 window");
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return None;
  }
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "X11 window created: handle=%lu",
           (unsigned long)win);
  log_print("INFO", log_msg);

  XClassHint class_hint = {.res_name = "peruere", .res_class = "peruere"};
  XSetClassHint(x11_display, win, &class_hint);

  Atom net_wm_window_type =
      XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
  Atom net_wm_window_type_desktop =
      XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
  XChangeProperty(x11_display, win, net_wm_window_type, XA_ATOM, 32,
                  PropModeReplace, (unsigned char *)&net_wm_window_type_desktop,
                  1);

  Atom net_wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
  Atom net_wm_state_below =
      XInternAtom(x11_display, "_NET_WM_STATE_BELOW", False);
  XChangeProperty(x11_display, win, net_wm_state, XA_ATOM, 32, PropModeAppend,
                  (unsigned char *)&net_wm_state_below, 1);

  Atom net_wm_state_sticky =
      XInternAtom(x11_display, "_NET_WM_STATE_STICKY", False);
  XChangeProperty(x11_display, win, net_wm_state, XA_ATOM, 32, PropModeAppend,
                  (unsigned char *)&net_wm_state_sticky, 1);

  Atom motif_wm_hints = XInternAtom(x11_display, "_MOTIF_WM_HINTS", False);
  if (motif_wm_hints != None) {
    long hints[5] = {2, 0, 0, 0, 0};
    XChangeProperty(x11_display, win, motif_wm_hints, motif_wm_hints, 32,
                    PropModeReplace, (unsigned char *)hints, 5);
  }

  XStoreName(x11_display, win, "peruere");

  Region region = XCreateRegion();
  XShapeCombineRegion(x11_display, win, ShapeInput, 0, 0, region, ShapeSet);
  XDestroyRegion(region);

  XMapWindow(x11_display, win);
  XLowerWindow(x11_display, win);
  XFlush(x11_display);
  log_print("INFO", "X11 window displayed and bottomed");

  return win;
}

// 新增：MPV日志开关回调（动态更新日志级别）
static void on_mpv_log_toggled(GtkWidget *widget, gpointer data) {
  gboolean enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  if (mpv_inst) {
    // 动态设置MPV日志级别
    const char *log_level = enabled ? "all=v" : "all=error";
    if (mpv_set_option_string(mpv_inst, "msg-level", log_level) < 0) {
      log_print("WARNING", "Failed to update MPV log level");
    }
    // 重新请求日志消息
    mpv_request_log_messages(mpv_inst, enabled ? "trace" : "error");
    log_print("INFO",
              enabled ? "MPV debug log enabled" : "MPV debug log disabled");
  }
}

// MPV事件循环
static void *mpv_event_loop(void *arg) {
  (void)arg;
  log_print("INFO", "MPV event thread started");
  while (!quit_flag && is_playing) {
    mpv_event *event = mpv_wait_event(mpv_inst, 100);
    if (!event)
      continue;

    switch (event->event_id) {
    case MPV_EVENT_LOG_MESSAGE: {
      // 根据开关控制MPV日志输出
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chk_mpv_log))) {
        mpv_event_log_message *msg = (mpv_event_log_message *)event->data;
        char log_msg[1024];
        snprintf(log_msg, sizeof(log_msg), "[MPV] [%s] %s", msg->prefix,
                 msg->text);
        log_print("DEBUG", log_msg);
      }
      break;
    }
    case MPV_EVENT_SHUTDOWN:
      log_print("INFO", "MPV closed");
      quit_flag = 1;
      // 禁用停止按钮（GTK主线程）
      SensitiveData *sd = g_new(SensitiveData, 1);
      sd->widget = btn_play;
      sd->sensitive = TRUE;
      g_idle_add(set_sensitive_idle, sd);
      pthread_mutex_lock(&state_mutex);
      is_playing = 0;
      pthread_mutex_unlock(&state_mutex);
      break;
    case MPV_EVENT_END_FILE:
      log_print("INFO", "Video playback ended, looping...");
      break;
    case MPV_EVENT_FILE_LOADED:
      log_print("INFO", "Video file loaded successfully");
      break;
    default: {
      char log_msg[256];
      snprintf(log_msg, sizeof(log_msg), "MPV event: %d", event->event_id);
      log_print("INFO", log_msg);
      break;
    }
    }
  }
  log_print("INFO", "MPV event thread exited");
  return NULL;
}

// 播放视频（提前定义，解决依赖）
static int mpv_play_video(const char *play_path, int width, int height, int x,
                          int y) {
  setenv("LC_NUMERIC", "C", 1);
  setlocale(LC_NUMERIC, "C");

  x11_window = create_x11_window(width, height, x, y);
  if (x11_window == None)
    return -1;

  mpv_inst = mpv_create();
  if (!mpv_inst) {
    set_error("Failed to create MPV instance (check libmpv installation)");
    char log_msg[256];
    snprintf(log_msg, sizeof(log_msg),
             "MPV create failed. Check if libmpv is installed");
    log_print("ERROR", log_msg);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return -1;
  }
  log_print("INFO", "MPV instance created");

  // 初始设置MPV日志级别（跟随开关状态）
  gboolean mpv_log_enabled =
      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chk_mpv_log));
  const char *log_level = mpv_log_enabled ? "all=v" : "all=error";
  if (mpv_set_option_string(mpv_inst, "msg-level", log_level) < 0) {
    log_print("WARNING", "Failed to set MPV log level");
  }

  if (mpv_set_option_string(mpv_inst, "loop", "yes") < 0) {
    set_error("Failed to set loop");
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return -1;
  }
  if (mpv_set_option_string(mpv_inst, "x11-bypass-compositor", "yes") < 0) {
    set_error("Failed to set bypass compositor");
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return -1;
  }
  if (mpv_set_option_string(mpv_inst, "vo", "gpu") < 0) {
    log_print("WARNING", "Failed to set vo=gpu, trying x11");
    if (mpv_set_option_string(mpv_inst, "vo", "x11") < 0) {
      set_error("Failed to set video output (vo)");
      mpv_terminate_destroy(mpv_inst);
      XCloseDisplay(x11_display);
      x11_display = NULL;
      return -1;
    }
  }
  log_print("INFO", "MPV basic options set");

  int64_t wid = (int64_t)x11_window;
  if (mpv_set_property(mpv_inst, "wid", MPV_FORMAT_INT64, &wid) < 0) {
    set_error("Failed to bind MPV to window");
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return -1;
  }
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "MPV bound to X11 window: wid=%ld", wid);
  log_print("INFO", log_msg);

  if (mpv_initialize(mpv_inst) < 0) {
    set_error("Failed to initialize MPV");
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    return -1;
  }
  // 初始请求日志消息
  mpv_request_log_messages(mpv_inst, mpv_log_enabled ? "trace" : "error");
  log_print("INFO", "MPV initialized");

  // 启用停止按钮（必须在GTK主线程执行）
  SensitiveData *sd1 = g_new(SensitiveData, 1);
  sd1->widget = btn_stop;
  sd1->sensitive = TRUE;
  g_idle_add(set_sensitive_idle, sd1);
  pthread_mutex_lock(&state_mutex);
  is_playing = 1;
  pthread_mutex_unlock(&state_mutex);

  if (pthread_create(&mpv_event_thread, NULL, mpv_event_loop, NULL) != 0) {
    set_error("Failed to create MPV event thread");
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    // 禁用停止按钮
    SensitiveData *sd = g_new(SensitiveData, 1);
    sd->widget = btn_stop;
    sd->sensitive = FALSE;
    g_idle_add(set_sensitive_idle, sd);
    pthread_mutex_lock(&state_mutex);
    is_playing = 0;
    pthread_mutex_unlock(&state_mutex);
    return -1;
  }

  const char *cmd[] = {"loadfile", play_path, NULL};
  if (mpv_command_async(mpv_inst, 0, cmd) < 0) {
    set_error("Failed to load video");
    pthread_cancel(mpv_event_thread);
    pthread_join(mpv_event_thread, NULL);
    mpv_terminate_destroy(mpv_inst);
    XCloseDisplay(x11_display);
    x11_display = NULL;
    // 禁用停止按钮
    SensitiveData *sd2 = g_new(SensitiveData, 1);
    sd2->widget = btn_stop;
    sd2->sensitive = FALSE;
    g_idle_add(set_sensitive_idle, sd2);
    pthread_mutex_lock(&state_mutex);
    is_playing = 0;
    pthread_mutex_unlock(&state_mutex);
    return -1;
  }
  snprintf(log_msg, sizeof(log_msg), "Load video command sent: %s", play_path);
  log_print("INFO", log_msg);
  set_error("Video playing");
  return 0;
}

// 下载工作线程
static void *download_worker(void *arg) {
  char *url = (char *)arg;
  char *play_path = NULL;

  if (download_url_thread(url, &play_path) != 0) {
    g_free(url);
    return NULL;
  }

  if (quit_flag) {
    g_free(url);
    return NULL;
  }

  int win_width, win_height, win_x, win_y;
  if (parse_geometry(cfg.geometry, &win_width, &win_height, &win_x, &win_y) !=
      0) {
    g_free(url);
    return NULL;
  }

  if (mpv_play_video(play_path, win_width, win_height, win_x, win_y) != 0) {
    set_error("Failed to play video");
  }

  g_free(url);
  return NULL;
}

// 清理临时文件
static void cleanup_temp_file(void) {
  if (temp_fd >= 0) {
    close(temp_fd);
    temp_fd = -1;
    log_print("INFO", "Temporary file handle closed");
  }
  if (strlen(temp_path) > 0 && access(temp_path, F_OK) == 0) {
    if (unlink(temp_path) == 0) {
      set_error("Temporary file cleaned");
      log_print("INFO", temp_path);
    } else {
      set_error("Failed to clean temporary file");
    }
    temp_path[0] = '\0';
  }
}

// 资源清理
static void cleanup_peruere_resources() {
  pthread_mutex_lock(&state_mutex);
  int downloading = is_downloading;
  int playing = is_playing;
  pthread_mutex_unlock(&state_mutex);

  if (downloading) {
    is_downloading = 0;
    if (curl_handle)
      curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 1L);
    pthread_join(download_thread, NULL);
  }

  if (playing) {
    is_playing = 0;
    // 禁用停止按钮（GTK主线程）
    SensitiveData *sd1 = g_new(SensitiveData, 1);
    sd1->widget = btn_stop;
    sd1->sensitive = FALSE;
    g_idle_add(set_sensitive_idle, sd1);
    if (mpv_inst) {
      mpv_command_async(mpv_inst, 0, (const char *[]){"quit", NULL});
      pthread_join(mpv_event_thread, NULL);
      mpv_terminate_destroy(mpv_inst);
      mpv_inst = NULL;
    }
  }

  if (x11_display) {
    XCloseDisplay(x11_display);
    x11_display = NULL;
    x11_window = None;
  }

  cleanup_temp_file();

  if (curl_handle) {
    curl_easy_cleanup(curl_handle);
    curl_handle = NULL;
  }

  // 启用播放按钮（GTK主线程）
  SensitiveData *sd2 = g_new(SensitiveData, 1);
  sd2->widget = btn_play;
  sd2->sensitive = TRUE;
  g_idle_add(set_sensitive_idle, sd2);
}

// 文件选择对话框
static void open_file_dialog(GtkWidget *widget, gpointer data) {
  GtkWidget *dialog = gtk_file_chooser_dialog_new(
      "Select Video File", GTK_WINDOW(main_window),
      GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open",
      GTK_RESPONSE_ACCEPT, NULL);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
    char *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
    if (path) {
      if (strlen(path) >= 1023) {
        path[1023] = '\0';
        set_error("File path truncated (too long)");
      }
      gtk_entry_set_text(GTK_ENTRY(entry_file), path);
      input_history_save(&hist_file, path);
      // 手动触发输入状态更新（确保Keep Cache禁用）
      update_input_states();
      g_free(path);
    }
  }

  gtk_widget_destroy(dialog);
}

// 停止按钮处理（提前定义，解决依赖）
static gboolean stop_idle_func(gpointer data) {
  gtk_widget_set_sensitive(btn_play, TRUE);
  gtk_widget_set_sensitive(btn_stop, FALSE);
  set_error("Playback stopped");
  set_download_progress(-1);
  return FALSE;
}

static void on_stop_clicked(GtkWidget *w, gpointer d) {
  quit_flag = 1;
  cleanup_peruere_resources();
  quit_flag = 0;

  g_idle_add(stop_idle_func, NULL);
  log_print("INFO", "Playback stopped");
}

// 播放按钮点击
static void on_play_clicked(GtkWidget *widget, gpointer data) {
  on_stop_clicked(NULL, NULL);

  const char *file_path = gtk_entry_get_text(GTK_ENTRY(entry_file));
  const char *url = gtk_entry_get_text(GTK_ENTRY(entry_url));
  const char *geometry = gtk_entry_get_text(GTK_ENTRY(entry_geo));
  cfg.keep_cache =
      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chk_keep_cache));

  if ((strlen(file_path) == 0 && strlen(url) == 0) ||
      (strlen(file_path) > 0 && strlen(url) > 0)) {
    set_error("Select local file OR URL (not both)");
    return;
  }

  strncpy(cfg.file_path, file_path, 1023);
  strncpy(cfg.url, url, 1023);
  strncpy(cfg.geometry, geometry, 31);
  cfg.file_path[1023] = '\0';
  cfg.url[1023] = '\0';
  cfg.geometry[31] = '\0';

  int win_width, win_height, win_x, win_y;
  if (parse_geometry(cfg.geometry, &win_width, &win_height, &win_x, &win_y) !=
      0)
    return;

  pthread_mutex_lock(&state_mutex);
  gtk_widget_set_sensitive(btn_play, FALSE);
  pthread_mutex_unlock(&state_mutex);

  if (strlen(file_path) > 0) {
    struct stat st;
    if (stat(file_path, &st) != 0) {
      char err_msg[256];
      snprintf(err_msg, sizeof(err_msg), "File not found: %s", file_path);
      set_error(err_msg);
      pthread_mutex_lock(&state_mutex);
      gtk_widget_set_sensitive(btn_play, TRUE);
      pthread_mutex_unlock(&state_mutex);
      return;
    }
    if (st.st_size == 0) {
      set_error("File is empty");
      pthread_mutex_lock(&state_mutex);
      gtk_widget_set_sensitive(btn_play, TRUE);
      pthread_mutex_unlock(&state_mutex);
      return;
    }

    char log_msg[256];
    snprintf(log_msg, sizeof(log_msg), "Local file verified: %s Size: %.2f MB",
             file_path, (double)st.st_size / 1024 / 1024);
    log_print("INFO", log_msg);

    if (mpv_play_video(file_path, win_width, win_height, win_x, win_y) != 0) {
      set_error("Failed to play local file");
      pthread_mutex_lock(&state_mutex);
      gtk_widget_set_sensitive(btn_play, TRUE);
      pthread_mutex_unlock(&state_mutex);
    }
    return;
  }

  pthread_mutex_lock(&state_mutex);
  is_downloading = 1;
  pthread_mutex_unlock(&state_mutex);

  set_error("Downloading...");
  set_download_progress(0);

  if (pthread_create(&download_thread, NULL, download_worker, g_strdup(url)) !=
      0) {
    set_error("Failed to start download thread");
    pthread_mutex_lock(&state_mutex);
    is_downloading = 0;
    gtk_widget_set_sensitive(btn_play, TRUE);
    pthread_mutex_unlock(&state_mutex);
  }
}

// 清理缓存按钮
static void on_clean_cache_clicked(GtkWidget *widget, gpointer data) {
  on_stop_clicked(NULL, NULL);
  clean_all_cache();
}

// 帮助窗口
static gboolean on_help_key_press(GtkWidget *w, GdkEventKey *ev, gpointer d) {
  if (ev->keyval == GDK_KEY_Escape && w && GTK_IS_WINDOW(w)) {
    gtk_widget_destroy(w);
    return TRUE;
  }
  return FALSE;
}

static void on_help_destroy(GtkWidget *w, gpointer d) { help_window = NULL; }

static void show_help(GtkWidget *widget, gpointer data) {
  if (help_window) {
    gtk_window_present(GTK_WINDOW(help_window));
    return;
  }

  help_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(help_window), "Help");
  gtk_widget_set_size_request(help_window, 500, 300);
  gtk_window_set_transient_for(GTK_WINDOW(help_window),
                               GTK_WINDOW(main_window));
  gtk_window_set_modal(GTK_WINDOW(help_window), TRUE);

  GtkWidget *text_view = gtk_text_view_new();
  GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
  gtk_text_buffer_set_text(buffer, help_text, -1);
  gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_view), buffer);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);

  GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_add(GTK_CONTAINER(scrolled_win), text_view);
  gtk_container_add(GTK_CONTAINER(help_window), scrolled_win);

  g_signal_connect(help_window, "key-press-event",
                   G_CALLBACK(on_help_key_press), NULL);
  g_signal_connect(help_window, "destroy", G_CALLBACK(on_help_destroy), NULL);

  gtk_widget_show_all(help_window);
}

// 日志窗口
static void on_log_destroy(GtkWidget *w, gpointer d) { log_window = NULL; }

static void show_log_window(GtkWidget *widget, gpointer data) {
  if (log_window) {
    gtk_window_present(GTK_WINDOW(log_window));
    return;
  }

  log_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(log_window), "Debug Log");
  gtk_widget_set_size_request(log_window, 800, 600);
  gtk_window_set_transient_for(GTK_WINDOW(log_window), GTK_WINDOW(main_window));

  GtkWidget *text_view = gtk_text_view_new();
  log_buffer = gtk_text_buffer_new(NULL);
  gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_view), log_buffer);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);

  GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_container_add(GTK_CONTAINER(scrolled_win), text_view);
  gtk_container_add(GTK_CONTAINER(log_window), scrolled_win);

  g_signal_connect(log_window, "destroy", G_CALLBACK(on_log_destroy), NULL);

  gtk_widget_show_all(log_window);
  log_print("INFO", "Log window opened");
}

// 托盘菜单回调
static void on_show_window(GtkWidget *widget, gpointer data) {
  if (main_window && GTK_IS_WINDOW(main_window)) {
    gtk_widget_show_all(main_window);
    gtk_window_present(GTK_WINDOW(main_window));
  }
}

static void on_hide_window(GtkWidget *widget, gpointer data) {
  if (main_window && GTK_IS_WINDOW(main_window)) {
    gtk_widget_hide(main_window);
  }
}

static void on_quit_program(GtkWidget *widget, gpointer data) {
  quit_flag = 1;
  cleanup_peruere_resources();

  for (int i = 0; i < hist_file.count; i++)
    if (hist_file.history[i])
      free(hist_file.history[i]);
  for (int i = 0; i < hist_url.count; i++)
    if (hist_url.history[i])
      free(hist_url.history[i]);
  for (int i = 0; i < hist_geo.count; i++)
    if (hist_geo.history[i])
      free(hist_geo.history[i]);

  if (tray_indicator) {
    g_object_unref(tray_indicator);
    tray_indicator = NULL;
  }
  if (help_window)
    gtk_widget_destroy(help_window);
  if (log_window)
    gtk_widget_destroy(log_window);
  if (main_window)
    gtk_widget_destroy(main_window);

  log_print("INFO", "Program exited normally");
  gtk_main_quit();
}

static void tray_play_clicked(GtkWidget *widget, gpointer data) {
  on_play_clicked(NULL, NULL);
}

static void tray_stop_clicked(GtkWidget *widget, gpointer data) {
  on_stop_clicked(NULL, NULL);
}

// 初始化托盘
static void init_tray() {
  tray_indicator = app_indicator_new("video-desktop-player", "system-run",
                                     APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
  app_indicator_set_status(tray_indicator, APP_INDICATOR_STATUS_ACTIVE);

  GtkWidget *menu = gtk_menu_new();

  GtkWidget *play_item = gtk_menu_item_new_with_label("Play");
  g_signal_connect(play_item, "activate", G_CALLBACK(tray_play_clicked), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), play_item);

  GtkWidget *stop_item = gtk_menu_item_new_with_label("Stop");
  g_signal_connect(stop_item, "activate", G_CALLBACK(tray_stop_clicked), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), stop_item);

  GtkWidget *show_item = gtk_menu_item_new_with_label("Show Window");
  g_signal_connect(show_item, "activate", G_CALLBACK(on_show_window), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), show_item);

  GtkWidget *hide_item = gtk_menu_item_new_with_label("Hide Window");
  g_signal_connect(hide_item, "activate", G_CALLBACK(on_hide_window), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), hide_item);

  GtkWidget *log_item = gtk_menu_item_new_with_label("Show Log");
  g_signal_connect(log_item, "activate", G_CALLBACK(show_log_window), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), log_item);

  GtkWidget *quit_item = gtk_menu_item_new_with_label("Quit");
  g_signal_connect(quit_item, "activate", G_CALLBACK(on_quit_program), NULL);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit_item);

  gtk_widget_show_all(menu);
  app_indicator_set_menu(tray_indicator, GTK_MENU(menu));
}

// 初始化GUI
static void init_gui() {
  memset(&cfg, 0, sizeof(Config));
  strcpy(cfg.geometry, "1920x1080+0+0");
  cfg.keep_cache = false;

  input_history_init(&hist_file);
  input_history_init(&hist_url);
  input_history_init(&hist_geo);
  input_history_save(&hist_url, "");
  input_history_save(&hist_file, "");
  input_history_save(&hist_geo, cfg.geometry);

  main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(main_window), "Video Desktop Player");
  gtk_widget_set_size_request(main_window, 600, 520);
  gtk_window_set_resizable(GTK_WINDOW(main_window), FALSE);

  GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 15);
  gtk_container_set_border_width(GTK_CONTAINER(vbox), 20);
  gtk_container_add(GTK_CONTAINER(main_window), vbox);

  GtkWidget *title = gtk_label_new(NULL);
  gtk_label_set_markup(
      GTK_LABEL(title),
      "<span font='24' color='#444'>Video Desktop Player</span>");
  gtk_box_pack_start(GTK_BOX(vbox), title, FALSE, FALSE, 0);
  gtk_widget_set_halign(title, GTK_ALIGN_CENTER);

  GtkWidget *hbox_file = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_file, FALSE, FALSE, 0);
  GtkWidget *lbl_file = gtk_label_new("Local File:");
  gtk_box_pack_start(GTK_BOX(hbox_file), lbl_file, FALSE, FALSE, 0);
  entry_file = gtk_entry_new();
  gtk_entry_set_max_length(GTK_ENTRY(entry_file), 1023);
  gtk_widget_set_size_request(entry_file, 400, 30);
  gtk_box_pack_start(GTK_BOX(hbox_file), entry_file, TRUE, TRUE, 0);
  btn_browse = gtk_button_new_with_label("Browse...");
  gtk_widget_set_size_request(btn_browse, 80, 30);
  gtk_box_pack_start(GTK_BOX(hbox_file), btn_browse, FALSE, FALSE, 0);

  GtkWidget *hbox_url = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_url, FALSE, FALSE, 0);
  GtkWidget *lbl_url = gtk_label_new("URL:");
  gtk_box_pack_start(GTK_BOX(hbox_url), lbl_url, FALSE, FALSE, 0);
  entry_url = gtk_entry_new();
  gtk_entry_set_max_length(GTK_ENTRY(entry_url), 1023);
  gtk_widget_set_size_request(entry_url, 480, 30);
  gtk_box_pack_start(GTK_BOX(hbox_url), entry_url, TRUE, TRUE, 0);

  GtkWidget *hbox_geo = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_geo, FALSE, FALSE, 0);
  GtkWidget *lbl_geo = gtk_label_new("Window Geometry:");
  gtk_box_pack_start(GTK_BOX(hbox_geo), lbl_geo, FALSE, FALSE, 0);
  entry_geo = gtk_entry_new();
  gtk_entry_set_text(GTK_ENTRY(entry_geo), cfg.geometry);
  gtk_entry_set_max_length(GTK_ENTRY(entry_geo), 31);
  gtk_widget_set_size_request(entry_geo, 200, 30);
  gtk_box_pack_start(GTK_BOX(hbox_geo), entry_geo, FALSE, FALSE, 0);

  chk_keep_cache = gtk_check_button_new_with_label("Keep Cache");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_keep_cache),
                               cfg.keep_cache);
  gtk_box_pack_start(GTK_BOX(vbox), chk_keep_cache, FALSE, FALSE, 0);
  gtk_widget_set_margin_start(chk_keep_cache, 10);
  gtk_widget_set_sensitive(chk_keep_cache, FALSE); // 初始禁用

  // MPV日志开关（添加状态变化回调）
  chk_mpv_log = gtk_check_button_new_with_label("Show MPV Debug Log");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_mpv_log), FALSE);
  gtk_box_pack_start(GTK_BOX(vbox), chk_mpv_log, FALSE, FALSE, 0);
  gtk_widget_set_margin_start(chk_mpv_log, 10);
  g_signal_connect(chk_mpv_log, "toggled", G_CALLBACK(on_mpv_log_toggled),
                   NULL); // 绑定回调

  GtkWidget *hbox_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_buttons, FALSE, FALSE, 0);
  gtk_widget_set_halign(hbox_buttons, GTK_ALIGN_CENTER);
  btn_play = gtk_button_new_with_label("Play");
  gtk_widget_set_size_request(btn_play, 120, 40);
  gtk_box_pack_start(GTK_BOX(hbox_buttons), btn_play, FALSE, FALSE, 0);
  gtk_widget_set_sensitive(btn_play, FALSE); // 初始禁用

  btn_stop = gtk_button_new_with_label("Stop");
  gtk_widget_set_size_request(btn_stop, 120, 40);
  gtk_widget_set_sensitive(btn_stop, FALSE); // 初始禁用
  gtk_box_pack_start(GTK_BOX(hbox_buttons), btn_stop, FALSE, FALSE, 0);
  btn_clean = gtk_button_new_with_label("Clean Cache");
  gtk_widget_set_size_request(btn_clean, 120, 40);
  gtk_box_pack_start(GTK_BOX(hbox_buttons), btn_clean, FALSE, FALSE, 0);
  btn_help = gtk_button_new_with_label("Help");
  gtk_widget_set_size_request(btn_help, 120, 40);
  gtk_box_pack_start(GTK_BOX(hbox_buttons), btn_help, FALSE, FALSE, 0);

  GtkWidget *hbox_log_btn = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_log_btn, FALSE, FALSE, 0);
  gtk_widget_set_halign(hbox_log_btn, GTK_ALIGN_CENTER);
  btn_log = gtk_button_new_with_label("Show Log");
  gtk_widget_set_size_request(btn_log, 120, 30);
  gtk_box_pack_start(GTK_BOX(hbox_log_btn), btn_log, FALSE, FALSE, 0);

  GtkWidget *hbox_progress = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox_progress, FALSE, FALSE, 0);
  lbl_progress = gtk_label_new("");
  gtk_widget_set_halign(lbl_progress, GTK_ALIGN_CENTER);
  gtk_box_pack_start(GTK_BOX(hbox_progress), lbl_progress, FALSE, FALSE, 0);
  progress_bar = gtk_progress_bar_new();
  gtk_widget_set_size_request(progress_bar, 500, 15);
  gtk_widget_set_halign(progress_bar, GTK_ALIGN_CENTER);
  gtk_box_pack_start(GTK_BOX(hbox_progress), progress_bar, FALSE, FALSE, 0);
  gtk_widget_hide(lbl_progress);
  gtk_widget_hide(progress_bar);

  lbl_error = gtk_label_new(NULL);
  gtk_label_set_markup(GTK_LABEL(lbl_error), "<span color='red'></span>");
  gtk_box_pack_start(GTK_BOX(vbox), lbl_error, FALSE, FALSE, 0);
  gtk_widget_set_halign(lbl_error, GTK_ALIGN_CENTER);
  gtk_widget_hide(lbl_error);

  // 信号连接
  g_signal_connect(btn_browse, "clicked", G_CALLBACK(open_file_dialog), NULL);
  g_signal_connect(btn_play, "clicked", G_CALLBACK(on_play_clicked), NULL);
  g_signal_connect(btn_stop, "clicked", G_CALLBACK(on_stop_clicked), NULL);
  g_signal_connect(btn_clean, "clicked", G_CALLBACK(on_clean_cache_clicked),
                   NULL);
  g_signal_connect(btn_help, "clicked", G_CALLBACK(show_help), NULL);
  g_signal_connect(btn_log, "clicked", G_CALLBACK(show_log_window), NULL);

  g_signal_connect(entry_file, "key-press-event", G_CALLBACK(on_key_press),
                   &hist_file);
  g_signal_connect(entry_file, "changed", G_CALLBACK(on_entry_changed),
                   &hist_file);
  g_signal_connect(entry_url, "key-press-event", G_CALLBACK(on_key_press),
                   &hist_url);
  g_signal_connect(entry_url, "changed", G_CALLBACK(on_entry_changed),
                   &hist_url);
  g_signal_connect(entry_geo, "key-press-event", G_CALLBACK(on_key_press),
                   &hist_geo);
  g_signal_connect(entry_geo, "changed", G_CALLBACK(on_entry_changed),
                   &hist_geo);

  // 主窗口关闭事件（隐藏窗口而非退出）
  g_signal_connect(main_window, "delete-event", G_CALLBACK(on_hide_window),
                   NULL);
}

// 信号处理
static void signal_handler(int sig) {
  char log_msg[256];
  snprintf(log_msg, sizeof(log_msg), "Received exit signal: %d", sig);
  log_print("INFO", log_msg);
  set_error("Received exit signal");
  quit_flag = 1;
  cleanup_peruere_resources();
  gtk_main_quit();
}

// 主函数
int main(int argc, char *argv[]) {
  setenv("LC_ALL", "C", 1);
  setlocale(LC_ALL, "C");

  char *locale = setlocale(LC_NUMERIC, NULL);
  if (strcmp(locale, "C") != 0) {
    fprintf(stderr, "Warning: Failed to set LC_NUMERIC to C, current: %s\n",
            locale);
  }

  // 初始化互斥锁（之前依赖静态初始化，显式初始化更可靠）
  if (pthread_mutex_init(&progress_mutex, NULL) != 0 ||
      pthread_mutex_init(&state_mutex, NULL) != 0 ||
      pthread_mutex_init(&log_mutex, NULL) != 0) {
    fprintf(stderr, "Failed to initialize mutexes\n");
    return 1;
  }

  struct sigaction sa;
  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = signal_handler;
  sigaction(SIGINT, &sa, NULL);
  sigaction(SIGTERM, &sa, NULL);

  curl_global_init(CURL_GLOBAL_DEFAULT);

  if (!gtk_init_check(&argc, &argv)) {
    fprintf(stderr, "Failed to initialize GTK\n");
    curl_global_cleanup();
    pthread_mutex_destroy(&progress_mutex);
    pthread_mutex_destroy(&state_mutex);
    pthread_mutex_destroy(&log_mutex);
    return 1;
  }

  init_gui();
  init_tray();

  if (main_window)
    gtk_widget_show_all(main_window);

  log_print("INFO", "Program started successfully");
  gtk_main();

  // 资源清理
  curl_global_cleanup();
  pthread_mutex_destroy(&progress_mutex);
  pthread_mutex_destroy(&state_mutex);
  pthread_mutex_destroy(&log_mutex);

  return 0;
}
